#ifndef __TDP2_VECTOR_HPP__
#define __TDP2_VECTOR_HPP__

#include "tdp2.hpp"
#include "tdp2_allocator.hpp"
#include "tdp2_copier.hpp"

namespace TDP
{
	class PanicHandler;

	namespace Containers
	{
		template<class _Tcontents>
		class Vector
		{
		protected:
			RDX::ObjectManagement::Allocator _alloc;
			_Tcontents *_contents;
			LargeUInt _length;
			LargeUInt _capacity;
			LargeUInt _align;
			
			bool TryResizeStepped(LargeUInt newLength, const _Tcontents *defaultValues, LargeUInt stepping, typename Copier<typename _Tcontents>::TrySetFunc setFunc);

		public:
			Vector();
			explicit Vector(const RDX::ObjectManagement::Allocator &alloc);

			void SetAlign(LargeUInt align);

			void Resize(const PanicHandler *ph, LargeUInt newLength);
			void Resize(const PanicHandler *ph, LargeUInt newLength, const _Tcontents &defaultValue, typename Copier<typename _Tcontents>::TrySetFunc setFunc = Copier<typename _Tcontents>::SimpleTrySetFunc);
			void ResizeFlat(const PanicHandler *ph, LargeUInt newLength, const _Tcontents *defaultValues, typename Copier<typename _Tcontents>::TrySetFunc setFunc = Copier<typename _Tcontents>::SimpleTrySetFunc);
			
			bool TryResize(LargeUInt newLength);
			bool TryResize(LargeUInt newLength, const _Tcontents &defaultValue, typename Copier<_Tcontents>::TrySetFunc setFunc = Copier<_Tcontents>::SimpleTrySetFunc);
			bool TryResizeFlat(LargeUInt newLength, const _Tcontents *defaultValues, typename Copier<_Tcontents>::TrySetFunc setFunc = Copier<_Tcontents>::SimpleTrySetFunc);

			void Release();
			operator _Tcontents *();
			operator const _Tcontents *() const;
			LargeUInt Count() const;

			const _Tcontents *DataPtr() const;
			_Tcontents *DataPtr();
		};
	}
}

#include <new>
#include "tdp2_error.hpp"

namespace TDP
{
	namespace Containers
	{
		template<class _Tcontents>
		inline Vector<_Tcontents>::Vector(const RDX::ObjectManagement::Allocator &alloc)
		{
			_contents = NULL;
			_length = 0;
			_capacity = 0;
			_alloc = alloc;
			_align = static_cast<LargeUInt>(RDX_ALIGNOF(_Tcontents));
		}

		template<class _Tcontents>
		inline Vector<_Tcontents>::Vector()
			: _alloc(Memory::DefaultAllocator()), _contents(NULL), _length(0), _capacity(0)
		{
			_align = static_cast<LargeUInt>(RDX_ALIGNOF(_Tcontents));
		}

		template<class _Tcontents>
		inline void Vector<_Tcontents>::SetAlign(LargeUInt align)
		{
			_align = align;
		}
		
		template<class _Tcontents>
		inline bool Vector<_Tcontents>::TryResizeStepped(LargeUInt newLength, const _Tcontents *defaultValues, LargeUInt stepping, typename Copier<typename _Tcontents>::TrySetFunc setFunc)
		{
			// Delete cleared members
			while(_length > newLength)
			{
				_length--;
				_contents[_length].~_Tcontents();
			}

			_contents = static_cast<_Tcontents *>(_alloc.reallocFunc(_alloc.opaque, _contents, newLength * sizeof(_Tcontents), _align));
			if(!_contents && newLength)
				return false;
			_capacity = newLength;

			while(_length < newLength)
			{
				if(!setFunc(_contents[_length], *defaultValues))
					return false;

				defaultValues += stepping;
				_length++;
			}

			return true;
		}
		
		template<class _Tcontents>
		inline bool Vector<_Tcontents>::TryResize(LargeUInt newLength, const _Tcontents &defaultValue, typename Copier<typename _Tcontents>::TrySetFunc setFunc)
		{
			return TryResizeStepped(newLength, &defaultValue, 0, setFunc);
		}

		template<class _Tcontents>
		inline bool Vector<_Tcontents>::TryResizeFlat(LargeUInt newLength, const _Tcontents *defaultValues, typename Copier<typename _Tcontents>::TrySetFunc setFunc)
		{
			return TryResizeStepped(newLength, defaultValues, 1, setFunc);
		}

		template<class _Tcontents>
		inline bool Vector<_Tcontents>::TryResize(LargeUInt newLength)
		{
			// Delete cleared members
			while(_length > newLength)
			{
				_length--;
				_contents[_length].~_Tcontents();
			}
			
			_contents = static_cast<_Tcontents *>(_alloc.reallocFunc(_alloc.opaque, _contents, newLength * sizeof(_Tcontents), _align));
			if(!_contents && newLength)
				return false;
			_capacity = newLength;

			while(_length < newLength)
			{
				try
				{
					new (_contents + _length) _Tcontents;
				}
				catch(...)
				{
					(_contents + _length)->~_Tcontents();
					return false;
				}

				_length++;
			}

			return true;
		}
		
		template<class _Tcontents>
		inline void Vector<_Tcontents>::Resize(const PanicHandler *ph, LargeUInt newLength, const _Tcontents &defaultValue, typename Copier<typename _Tcontents>::TrySetFunc setFunc)
		{
			if(!TryResize(newLength, defaultValue, setFunc))
				TDP::Panic(ph, FatalErrors::ERR_AllocationFailed);
		}

		template<class _Tcontents>
		void Vector<_Tcontents>::ResizeFlat(const PanicHandler *ph, LargeUInt newLength, const _Tcontents *defaultValues, typename Copier<typename _Tcontents>::TrySetFunc setFunc)
		{
			if(!TryResizeFlat(newLength, defaultValues, setFunc))
				TDP::Panic(ph, FatalErrors::ERR_AllocationFailed);
		}

		template<class _Tcontents>
		inline void Vector<_Tcontents>::Resize(const PanicHandler *ph, LargeUInt newLength)
		{
			if(!TryResize(newLength))
				TDP::Panic(ph, FatalErrors::ERR_AllocationFailed);
		}
		
		template<class _Tcontents>
		inline void Vector<_Tcontents>::Release()
		{
			TryResize(0);
		}
		
		template<class _Tcontents>
		inline Vector<_Tcontents>::operator _Tcontents *()
		{
			return _contents;
		}

		template<class _Tcontents>
		inline Vector<_Tcontents>::operator const _Tcontents *() const
		{
			return _contents;
		}

		template<class _Tcontents>
		inline LargeUInt Vector<_Tcontents>::Count() const
		{
			return _length;
		}

		template<class _Tcontents>
		inline const _Tcontents *Vector<_Tcontents>::DataPtr() const
		{
			return _contents;
		}

		template<class _Tcontents>
		inline _Tcontents *Vector<_Tcontents>::DataPtr()
		{
			return _contents;
		}

	}
}

#endif
